package com.zut.service.impl;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.zut.config.WxPayConfig;
import com.zut.entity.OrderInfo;
import com.zut.enums.OrderStatus;
import com.zut.enums.wxpay.WxApiType;
import com.zut.enums.wxpay.WxNotifyType;
import com.zut.service.OrderInfoService;
import com.zut.service.PaymentInfoService;
import com.zut.service.WxPayService;
import com.zut.util.OrderNoUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private WxPayConfig wxPayConfig;

    @Autowired
    private CloseableHttpClient wxPayClient;

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    // 加锁
    private final ReentrantLock lock = new ReentrantLock();
    // native 支付——业务
    @Override
    public Map<String, Object> nativePay(Long productId) throws Exception {

        // 1、自己生成一个临时订单
        log.info("生成订单！！！");

        // 保存订单到数据库 （未支付）
        OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);
        if(orderInfo!=null && orderInfo.getCodeUrl()!=null){
            log.info("二维码已存在！！！");
            // 已经生成过二维码了 将订单号 code_url 返回给controller 便于发送给前端
            Map map2 = new HashMap<>();
            map2.put("codeUrl", orderInfo.getCodeUrl());
            map2.put("orderNo", orderInfo.getOrderNo());
            return map2;
        }


        // 2、调用统一下单Api.使用HttpClient对象发起调用
        // HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");// 硬编码，不建议
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));// 硬编码，不建议
//        HttpUtil.createPost
        // 设置参数  把数据封装到 map 中
        Map<String, Object> map = new HashMap<>();
        map.put("appid", wxPayConfig.getAppid());
        map.put("mchid", wxPayConfig.getMchId());
        map.put("description",orderInfo.getTitle());
        map.put("out_trade_no", orderInfo.getOrderNo());
        // 设置回调地址
        // https://500c-219-143-130-12.ngrok.io/api/wx-pay/native/notify
        // 找到我们，把支付结果通知给我们后台系统
        map.put("notify_url", wxPayConfig.getNotifyDomain() + WxNotifyType.NATIVE_NOTIFY.getType());

        // 设置金额
        Map amountMap = new HashMap<>();
        amountMap.put("currency", "CNY");
        amountMap.put("total", orderInfo.getTotalFee());
        map.put("amount", amountMap);
        // 把 map 变成 json
        Gson gson = new Gson();
        String jsonParams = gson.toJson(map);
        log.info("请求参数：{}", jsonParams);
        StringEntity entity = new StringEntity(jsonParams, "UTF-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        // 设置请求头
        httpPost.addHeader("Accept", "application/json"); // 接收 JSON

        CloseableHttpResponse response = null;
        try{
            // 完成签名  并执行 请求到微信
            // 得到一个结果  结果  封装到了 CloseableHttpResponse 对象中  这里面放的是 微信给我们返回的结果
            response = wxPayClient.execute(httpPost);
//        CloseableHttpResponse response = HttpClient.execute(httpPost);

            // 解析 response
            String stringAsBody = EntityUtils.toString(response.getEntity());
            System.out.println("stringAsBody = " + stringAsBody);

            // 获取返回过来的状态码
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode == 200){
                // 微信服务端验签成功，并进行了预处理订单，200表示正常并且有返回值
                log.info("请求成功，返回结果：{}", stringAsBody);
            } else if (statusCode == 204) {
                // 204 代表正常，但是没有返回值
                log.info("请求成功，但是没有返回值");
            }else{
                log.error("请求失败，错误码：{}", statusCode+",return body"+stringAsBody);
            }

            // 解析返回值，进行数据的封装，把结果给前端
            HashMap<String, String> resultMap = gson.fromJson(stringAsBody, HashMap.class);
            // 得到 code_url
            String codeUrl = resultMap.get("code_url");

            // 保存code_Url到数据库
            orderInfoService.saveCodeUrl(orderInfo.getOrderNo(),codeUrl);

            // 将订单号 code_url 返回给controller 便于发送给前端
            Map map2 = new HashMap<>();
            map2.put("codeUrl", codeUrl);
            map2.put("orderNo", orderInfo.getOrderNo());

            return map2;
        }finally {
            response.close();
        }
    }

    @Override
    public void procssOrder(HashMap<String, Object> bodyMap) {
        Gson gson = new Gson();
        // 处理订单
        log.info("处理订单");

        try {
            // 对数据进行解密  得到明文

            String plainText = decryptFrpmResource(bodyMap);
            // 从明文中 根据 返回的消息 更新数据库的信息
            // 更新支付状态，记录支付日志
            HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
            // 获取订单编号
            Object orderNo = plainTextMap.get("out_trade_no");

            // 判断
            // 对后台通知交互时，如果微信收到商户的应答不符合规范或超时，
            // 微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，但微信不保证通知最终能成功。
            // （通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m）
            // 如果订单已支付  直接返回，否则，更新订单和记录支付日志
            String orderStatus = orderInfoService.getOrderStatus(orderNo);

            // 如果获取到锁
            if(lock.tryLock()){
                try {
                    if(!(OrderStatus.NOTPAY.getType().equals(orderStatus))){
                        // 如果不是未支付,则返回
                        return;
                    }
                    // 更新订单状态
                    orderInfoService.updateStatusByOrderNo(orderNo,OrderStatus.SUCCESS);
                    // 记录支付日志  也就是说把明文中的信息进行存储进数据库当中
                    paymentInfoService.createPaymentInfo(plainText);
                } finally {
                    lock.unlock();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 对数据进行解密  得到明文
    private String decryptFrpmResource(HashMap<String, Object> bodyMap) throws Exception {
        // 得到 resource
        Map<String,String> resourceMap = (Map<String, String>) bodyMap.get("resource");
        // 获取密文
        String ciphertext = resourceMap.get("ciphertext");
        // 得到随机串 盐值
        String nonce = resourceMap.get("nonce");
        // 得到 associated_data
        String associatedData = resourceMap.get("associated_data");

        // 利用微信提供的解密工具进行解密
        // 创建解密对象的时候，需要转入 商户的 apiV3Key
        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        // 根据随机串 和 associated_data  对密文进行解密
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);
        log.info("解密后的明文：{}", plainText);
        return plainText;
    }
}